#include<stdio.h>

#include<Mesh_IO/Mesh_IO.h>
#include<Algorithm/intersection_curve_of_two_surfaces.h>
#include<Mesh/libcell_iterator.h>
#include<Mesh_IO/Mesh_IO.h>
#include<Algorithm/mesh_bool.h>
#include<libcell_macros.h>
#include "directories_and_files.h"

#define quote lib_cell_quote
#include<unistd.h>



static void my_chuli_texture(Mesh* m,Mesh* m1,Int_RB_Tree* tree)
{ 
    printf("chuli texture\n");
    OBJ_MESH_PROP* omp1=(OBJ_MESH_PROP*)(m1->prop);
    if(omp1->mtlibname==NULL||strlen(omp1->mtlibname)==0)
    {
        return;
    }
    OBJ_MESH_PROP* omp=(OBJ_MESH_PROP*)malloc(sizeof(OBJ_MESH_PROP));
    obj_mesh_prop_init(omp);   
    m->prop=omp;
    omp->mtlibname=(char*)malloc(sizeof(char)*(strlen(omp1->mtlibname)+1));
    strcpy(omp->mtlibname,omp1->mtlibname); 
    //printf("m1 mtl name:%s\n",omp1->mtlname);
    for(auto cit=m->c_begin(m);cit!=m->c_end(m);cit++)
    {
        template_c* c=quote(cit);
        template_c* c1=(template_c*)(tree->find(tree,c->id));
        OBJ_CELL_PROP* ocp1=(OBJ_CELL_PROP*)(c1->prop);
        OBJ_CELL_PROP* ocp=(OBJ_CELL_PROP*)malloc(sizeof(OBJ_CELL_PROP));
        obj_cell_prop_init(ocp);
        if(ocp1->mtlname!=NULL&&strlen(ocp1->mtlname)!=0)
        {
            ocp->mtlname=(char*)malloc(sizeof(char)*(strlen(ocp1->mtlname)+1)); 
            strcpy(ocp->mtlname,ocp1->mtlname);
        }
        if(ocp1->group_info!=NULL&&strlen(ocp1->group_info)!=0)
        {
            ocp->group_info=(char*)malloc(sizeof(char)*(strlen(ocp1->group_info)+1));
            strcpy(ocp->group_info,ocp1->group_info);
        }
        c->prop=ocp; 
        ocp->vt=(double**)malloc(sizeof(double*)*c->vertices_size);
        for(int i=0;i<c->vertices_size;i++)
        {
            double *coord=get_triangle_coordinate(c->vertices[i]->point,c1->vertices[0]->point,c1->vertices[1]->point,c1->vertices[2]->point);
            //printf("coord %lf %lf %lf\n",coord[0],coord[1],coord[2]);
            if(coord==NULL)
            {
                coord=(double*)malloc(sizeof(double)*3);
                coord[0]=1.0/3.0;coord[1]=1.0/3.0;coord[2]=1.0/3.0;
            }
            ocp->vt[i]=(double*)malloc(sizeof(double)*2);
            for(int j=0;j<2;j++)
            {
                ocp->vt[i][j]=coord[0]*ocp1->vt[0][j]+coord[1]*ocp1->vt[1][j]+coord[2]*ocp1->vt[2][j];
            } 
            free(coord); 
        }
    } 
}


static Mesh*  tailoring_result(Mesh* m,double **ps,int len )
{
    Mesh m1;
    Mesh_init(&m1);
    double **V=(double**)malloc(sizeof(double*)*len*2);
    for(int i=0;i<len;i++)
    {
        V[i]=(double*)malloc(sizeof(double)*3);
        V[i][0]=ps[i][0];V[i][1]=ps[i][1];V[i][2]=-250.0;
        V[i+len]=(double*)malloc(sizeof(double)*3);
        V[i+len][0]=ps[i][0];V[i+len][1]=ps[i][1];V[i+len][2]=250.0; 
    }
    int ** C=(int**)malloc(sizeof(int*)*len*2);
    for(int i=0;i<len;i++)
    {
        C[2*i+0]=(int*)malloc(sizeof(int)*4);
        C[2*i+0][0]=3;
        C[2*i+0][1]=i;C[2*i+0][2]=(i+1)%len;C[2*i+0][3]=i+len;
/////***********************
        C[2*i+1]=(int*)malloc(sizeof(int)*4);
        C[2*i+1][0]=3;
        C[2*i+1][1]=(i+1)%len+len;C[2*i+1][2]=i+len;C[2*i+1][3]=(i+1)%len;
    }

    int nums[3]={len*2,0,len*2};
    _ReadArray_(&m1,V,NULL,C,nums,3,2);

    for(int i=0;i<len;i++)
    {
        free(V[i]);free(V[i+len]);
        free(C[i*2+0]);free(C[i*2+1]);
    }
    free(V);free(C);
  
    Mesh2_Crossover_Point* mcp1=(Mesh2_Crossover_Point*)malloc(sizeof(Mesh2_Crossover_Point));
    Mesh2_Crossover_Point* mcp2=(Mesh2_Crossover_Point*)malloc(sizeof(Mesh2_Crossover_Point));
    mesh2_crossover_point_init(mcp1);mesh2_crossover_point_init(mcp2);


    Mesh* curve=get_intersection_lines_of_two_meshs(m,&m1,mcp1,mcp2);
    adjust_mesh_topology_of_cintersecting_curve(curve,mcp1,mcp2); 
    Mesh* m2=NULL;
    if(curve->num_f(curve)>0)
    {
        Node* n1=NULL;
        for(auto cit=m->c_begin(m);cit!=m->c_end(m);cit++)
        {
            n1=node_overlying(n1,quote(cit)); 
        }

        Int_RB_Tree* tree2=(Int_RB_Tree*)malloc(sizeof(Int_RB_Tree));
        int_rb_tree_init(tree2);
        Int_RB_Tree* tree3=(Int_RB_Tree*)malloc(sizeof(Int_RB_Tree));
        int_rb_tree_init(tree3);


        m2=remesh_along_intersecting_curve(n1,mcp1,curve,tree2,tree3);
        cut_along_curve_mesh(m2,curve,tree2,tree3); 
        my_chuli_texture(m2,m,tree3);
        printf("num c :%d  %d\n",m->num_c(m),m2->num_c(m2));
        //Mesh_free(m2);free(m2);
        int_rb_tree_free(tree2);int_rb_tree_free(tree3);
        free_node(n1);
    }
    
    printf("num f:%d\n",curve->num_f(curve)); 
    for(auto vit=curve->v_begin(curve);vit!=curve->v_end(curve);vit++)
    {
        free(quote(vit)->prop); quote(vit)->prop=NULL;
    } 
    Mesh_free(curve);free(curve);
    mesh2_crossover_point_free(mcp1);mesh2_crossover_point_free(mcp2);
    Mesh_free(&m1);
    return m2;
}



static double * get_coordinate_in_another_coordinate_system(double* p,double* a1,double *a2,double*a3)
{
    LB_Matrix* m=(LB_Matrix*)malloc(sizeof(LB_Matrix));
    lb_matrix_init_double(m,3,3);
    double* data=(double*)(m->data);
    data[0*3+0]=a1[0]; data[1*3+0]=a1[1];  data[2*3+0]=a1[2];   
    data[0*3+1]=a2[0]; data[1*3+1]=a2[1];  data[2*3+1]=a2[2];
    data[0*3+2]=a3[0]; data[1*3+2]=a3[1];  data[2*3+2]=a3[2];
    LB_Matrix* mi=m->inverse(m);
    if(mi==NULL)
    {
        lb_matrix_free(m);
        return NULL;
    } 
    data=(double*)(mi->data);
    double * re=(double*)malloc(sizeof(double)*3); 
    memset(re,0,sizeof(double)*3);
    for(int i=0;i<3;i++)
    {
        re[i]=0;
        for(int j=0;j<3;j++)
        {
            re[i]+=data[i*3+j]*p[j];
        } 
    }
    //m->mult(m,mi);
    //m->print_self(m); 


    lb_matrix_free(mi);
    lb_matrix_free(m);
    return re;
}

static int is_point_in_triangle1(double* p,double *a1,double *a2,double *a3)
{
    double a[3]={a2[0]-a1[0],a2[1]-a1[1],a2[2]-a1[2]};
    double b[3]={a3[0]-a1[0],a3[1]-a1[1],a3[2]-a1[2]};
    double pp[3]={p[0]-a1[0],p[1]-a1[1],p[2]-a1[2]};
    double * n= out_product(a,b);
    if(n==NULL)
    {
        return 0;
    } 
    double *coord=get_coordinate_in_another_coordinate_system(pp,a,b,n);
    free(n);
    if(coord==NULL)
    {
        return 0;
    }
    int re=0;
    if(coord[0]>=1e-4&&coord[1]>=1e-4&&coord[0]+coord[1]<=1-1e-4)
    {  

        re=1;
    } 
    //printf("coord %lf %lf\n",coord[0],coord[1]);

    free(coord);
    return re;
}

static int is_point_in_polygon(double*p,double**ps,int len,int** s )
{
    for(int i=0;i<len-2;i++)
    {
        //printf("mark is %d\n",mark);
        //printf("%d %d %d\n",s[i][0],s[i][1],s[i][2]);
        if(is_point_in_triangle1(p,ps[s[i][0]],ps[s[i][1]],ps[s[i][2]])==1)
        {
            //printf("is point in triangle %d\n",mark);
            return 1; 
        } 
    }
    return 0;
}

static int is_cell_in_polygon(Mesh*m,template_c*c,double**ps,int len,int** s)
{
    for(auto cvit=m->cv_begin(m,*c);cvit!=m->cv_end(m,*c);cvit++)
    {   
        if(is_point_in_polygon(quote(cvit)->point,ps,len,s)==0)
        {
            return 0;
        }
    } 
    return 1;
}
static Node* find_connect_cells1(Mesh*m,Int_RB_Tree*tree,template_c*c)
{
    Node* re=NULL,*node1=NULL; 
    if(c==NULL)
    {
        printf("first cell is NULL ");
        return re;
    }
    Int_RB_Tree* tree1=(Int_RB_Tree*)malloc(sizeof(Int_RB_Tree));
    int_rb_tree_init(tree1);
    re=node_overlying(re,c);
    node1=node_overlying(node1,c);
    int flag=1;
    tree1->insert(tree1,c->id,&flag);
    tree->erase(tree,c->id);
    while(node1!=NULL)
    {
        Node* temp_n=NULL;
        for(Node* nit=node1;nit!=NULL;nit=(Node*)(nit->Next))
        {
            template_c*c2=(template_c*)(nit->value);
            for(auto chfit=m->chf_begin(m,*c2);chfit!=m->chf_end(m,*c2);chfit++)
            {
                template_c* c1=m->s_opposite_halfface(quote(chfit))->cell;
                if(c1!=NULL&&tree1->find(tree1,c1->id)==NULL)
                {
                    temp_n=node_overlying(temp_n,c1);
                    re=node_overlying(re,c1);
                    tree1->insert(tree1,c1->id,&flag);
                    tree->erase(tree,c1->id);
                }
            }
        } 
        free_node(node1);
        node1=temp_n;
    }
    //printf("here\n");
    int_rb_tree_free(tree1);
    return re;
}

static Node* mytest_m1(Mesh* m,double**ps,int len)
{
    if(m==NULL)
    {
        return NULL;
    }
    Node*re=NULL;
    int **s=(int**)malloc(sizeof(int*)*(len-2));
    for(int i=0;i<len-2;i++)
    {
        s[i]=(int*)malloc(sizeof(int)*3); 
        memset(s[i],0,sizeof(int)*3);
    } 
    optimization_subdivision_of_polygon(ps,len,s); 
    Int_RB_Tree*tree=(Int_RB_Tree*)malloc(sizeof(Int_RB_Tree));
    int_rb_tree_init(tree); 
    for(auto cit=m->c_begin(m);cit!=m->c_end(m);cit++)
    {
        if(is_cell_in_polygon(m,quote(cit),ps,len,s)==1)
        {
            tree->insert(tree,quote(cit)->id,quote(cit));
        }
    }
    printf("tree size :%d\n",tree->size);
    int len1=tree->size;
    while(tree->size>0)
    {
        int size=tree->size;
        auto it=tree->begin(tree);
        template_c* c=(template_c*)(it.second);
        Node* connect_cells=find_connect_cells1(m,tree,c);
        int size1=tree->size;
        int size2=node_size(connect_cells);
        if(size-size1>0.9*size2&&size2>=len1*0.4)
        {
            Node* rever=node_reverse(connect_cells);
            rever->Next=re;
            if(re!=NULL)
            {
                re->Prev=rever;
            }
            re=connect_cells;
        } 
        else
        {
            free_node(connect_cells);
        }
        //printf("size1:%d size2:%d size:%d\n",size1,size2,size);
    }  
    int_rb_tree_free(tree);
    for(int i=0;i<len-2;i++)
    {
        free(s[i]);
    }
    free(s);
    return re; 
}

static void export_cut_result(Mesh* m,double** ps,int len ,char* outfilename)
{

    Mesh* tai=tailoring_result(m,ps,4);
    Node*n=mytest_m1(tai,ps,4);
    if(n!=NULL)
    {
        printf("n is not NULL\n");
        export_obj_from_node(n,outfilename,((OBJ_MESH_PROP*)(tai->prop))->mtlibname); 
    }
    free_node(n);
    if(tai!=NULL)
    {
        for(auto cit=tai->c_begin(tai);cit!=tai->c_end(tai);cit++)
        {
            obj_cell_prop_free((OBJ_CELL_PROP*)(quote(cit)->prop));quote(cit)->prop=NULL;
        }
        free(tai->prop);
        Mesh_free(tai);free(tai);
    }
}

int  tailing_test(char* mesh_path,char* out_path,double**ps,int len)
{
    Mesh mesh;
    Mesh_init(&mesh); 

    _ReadObj_(&mesh,mesh_path);
    
    export_cut_result(&mesh,ps,len,out_path); 
    printf("end export cut result\n");
    for(auto cit=mesh.c_begin(&mesh);cit!=mesh.c_end(&mesh);cit++)
    {
        obj_cell_prop_free((OBJ_CELL_PROP*)(quote(cit)->prop));
        quote(cit)->prop=NULL;
    }
    obj_mesh_prop_free((OBJ_MESH_PROP*)(mesh.prop));mesh.prop=NULL;
    Mesh_free(&mesh);

    printf("endhere\n");
    return 1;
}
// void zydl_test(char*path,double**ps,int len)
// {
//     int count_files=0;
//     char** files=search_dir_files(path,&count_files);
//     for(int i=0;i<count_files;i++)
//     {
//         int temp_len=strlen(files[i]);
//         if(temp_len>4&&files[i][temp_len-1]=='j'&&files[i][temp_len-2]=='b'&&files[i][temp_len-3]=='o'&&files[i][temp_len-4]=='.') 
//         {
//             char file[200]={0};
//             strcat(file,path);
//             strcat(file,"/");
//             strcat(file,files[i]);
//             printf("%s\n",file); 
//             Mesh mesh;
//             Mesh_init(&mesh);
//             _ReadObj_(&mesh,file);
//             char outfile[200]={0};
//             strcat(outfile,path);
//             strcat(outfile,"/");
//             strcat(outfile,files[i]);
//             export_cut_result(&mesh,ps,len,file);
//             for(auto cit=mesh.c_begin(&mesh);cit!=mesh.c_end(&mesh);cit++)
//             {
//                 obj_cell_prop_freen(quote(cit));
//             }
//             Mesh_free(&mesh);
//             obj_mesh_prop_free((OBJ_MESH_PROP*)(mesh.prop));mesh.prop=NULL;
//         }
//     } 
//     for(int i=0;i<count_files;i++)
//     {
//         free(files[i]);
//     }
//     free(files);
// }

int main()
{
    double* quad[4]={NULL};
    quad[0]=(double*)malloc(sizeof(double)*3);
    quad[1]=(double*)malloc(sizeof(double)*3);
    quad[2]=(double*)malloc(sizeof(double)*3);
    quad[3]=(double*)malloc(sizeof(double)*3);
    
 //A1.txt
    quad[0][0]=-19.26078697;quad[0][1]=-103.92687376;quad[0][2]=0;
    quad[1][0]=387.71741855;quad[1][1]=-103.92687376;quad[1][2]=0; 
    quad[2][0]=387.71741855;quad[2][1]=140.29763709;quad[2][2]=0;
    quad[3][0]=-19.26078697;quad[3][1]=140.29763709;quad[3][2]=0;
    tailing_test("texture_ABXY.obj","test.obj",quad,4);
    


    printf("end1\n");
    pause();
    printf("end \n");
    return 0;
}
